Skip to content

Go 语言的并发编程是其最大的特色之一,主要通过 goroutineschannels 来实现。下面我将介绍这两个概念及其它相关的并发机制

一、Goroutines

goroutine 是 Go 语言中实现并发的基本单位。它是轻量级的线程,由 Go 运行时管理。

创建 Goroutine 只需在函数调用前加上 go 关键字,即可新开一个 goroutine。

go
package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		fmt.Println(s)
	}
}

func main() {
	go say("world")
	time.Sleep(time.Second)
}

二、 Channels

channel 是用来在 goroutines 之间传递消息的管道。可以通过它发送和接收不同类型的值,从而实现数据的同步交流。

创建和使用 Channel

go
package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // 将 sum 发送到 channel c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // 从 channel c 接收

	fmt.Println(x, y, x+y)
}

三、Select

select 语句用于在多个 channel 操作中进行选择。它类似于 switch 语句,但是每个 case 都是一个通信操作。

使用 Select

go
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		c1 <- "one"
	}()
	go func() {
		time.Sleep(2 * time.Second)
		c2 <- "two"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}

四、Sync

sync 包提供了基本的同步原语,如互斥锁(Mutex)和等待组(WaitGroup)。

使用 WaitGroup

go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("Worker %d starting\n", id)

    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
}

Go 的并发模型被设计得简单而强大,旨在使并发编程更加容易和安全。通过 goroutines, channels, select, 和 sync 等机制,可以构建高效且易于维护的并发程序。

木川工作室 (微信:mcmc2024)